package com.example.customclockdemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.Calendar;

public class ClockView extends View {

    private final String TAG = getClass().getSimpleName();
    private int mWidth = dp2Px(48);
    private int mHeight = dp2Px(48);
    private int mCenterX; // 中心点X坐标
    private int mCenterY; // 中心点Y坐标
    // 默认绘画，包括表圆盘、刻度
    private Paint mDefaultPaint;
    // 指针绘画
    private Paint mPointerPaint;
    // 文字绘画
    private Paint mTextPaint;

    // 时钟半径
    private float mRadius;
    // 时钟中心点半径
    private float mPointRadius;
    // 默认刻度长度
    private float mDefaultScaleLength;
    // 默认刻度宽度
    private float mDefaultScaleWidth;
    // 特殊刻度长度
    private float mParticularScaleLength;
    // 特殊刻度宽度
    private float mParticularScaleWidth;
    // 时针长度
    private float mHourPointerLength;
    // 时针宽度
    private float mHourPointerWidth;
    // 分针长度
    private float mMinutePointerLength;
    // 分针宽度
    private float mMinutePointerWidth;
    // 秒针长度
    private float mSecondPointerLength;
    // 秒针宽度
    private float mSecondPointerWidth;

    // 当前时
    private int mH;
    // 当前分
    private int mM;
    // 当前秒
    private int mS;

    // 时钟颜色
    private int mClockColor;
    // 默认刻度颜色
    private int mDefaultScaleColor;
    // 特殊刻度颜色
    private int mParticularScaleColor;
    // 时针颜色
    private int mHourPointerColor;
    // 分针颜色
    private int mMinutePointerColor;
    // 秒针颜色
    private int mSecondPointerColor;


    public ClockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 获取自定义属性值
        getAttrs(context, attrs);
        // 初始化绘画
        initPaint();
        // 开始计时
        startTime();
    }

    /**
     * 获取自定义属性
     * @param context
     * @param attrs
     */
    private void getAttrs(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ClockView);
        mClockColor = array.getColor(R.styleable.ClockView_clockColor, Color.BLACK);
        mDefaultScaleColor = array.getColor(R.styleable.ClockView_defaultScaleColor, mClockColor);
        mParticularScaleColor = array.getColor(R.styleable.ClockView_particularScaleColor, mClockColor);
        mHourPointerColor = array.getColor(R.styleable.ClockView_hourPointerColor, mClockColor);
        mMinutePointerColor = array.getColor(R.styleable.ClockView_minutePointerColor, mClockColor);
        mSecondPointerColor = array.getColor(R.styleable.ClockView_secondPointerColor, mClockColor);
    }

    /**
     * 初始化绘画
     */
    private void initPaint() {
        /**
         * 初始化表盘的绘画
         */
        mDefaultPaint = new Paint();
        mDefaultPaint.setAntiAlias(true); // 设置抗锯齿
        mDefaultPaint.setStyle(Paint.Style.STROKE); // Paint.Style.STROKE 设置只绘制图形的边

        /**
         * 初始化指针的绘画
         */
        mPointerPaint = new Paint();
        mPointerPaint.setAntiAlias(true); // 设置抗锯齿
        mPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE); // FILL_AND_STROKE 都绘制
        mPointerPaint.setTextSize(14);
        mPointerPaint.setStrokeCap(Paint.Cap.ROUND); // Paint.Cap.ROUND 线段的开始和结束的是圆形的

        /**
         * 初始化文字的绘画
         */
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true); // 设置抗锯齿
//        mTextPaint.setStyle(Paint.Style.FILL); // Paint.Style.FILL 设置只绘制图形内容
        mTextPaint.setColor(mClockColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 坐标原点移动到View中心
        canvas.translate(mCenterX, mCenterY);
        // 绘制表盘
        drawCircle(canvas);
        // 绘制指针
        drawPointer(canvas);
        // 绘制文本
        drawText(canvas);
    }

    /**
     * 绘制时钟的圆形及刻度
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        mDefaultPaint.setStrokeWidth(mDefaultScaleWidth);
        mDefaultPaint.setColor(mClockColor);
        canvas.drawCircle(0, 0, mRadius, mDefaultPaint);

        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) { // 特殊时刻
                mDefaultPaint.setStrokeWidth(mParticularScaleWidth);
                mDefaultPaint.setColor(mParticularScaleColor);
                canvas.drawLine(0, -mRadius, 0, -mRadius + mParticularScaleLength, mDefaultPaint);
            } else { // 普通时刻
                mDefaultPaint.setStrokeWidth(mDefaultScaleWidth);
                mDefaultPaint.setColor(mDefaultScaleColor);
                canvas.drawLine(0,-mRadius,0,-mRadius+mDefaultScaleLength,mDefaultPaint);
            }
            canvas.rotate(6);
        }

    }

    /**
     * 绘制特殊时刻的文字（12点、3点、6点、9点）
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        mTextPaint.setStrokeWidth(mDefaultScaleWidth / 2);
        mTextPaint.setTextSize(mParticularScaleWidth * 4);
        // 文字绘制中心点移动到横向中心
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); // 安卓字体属性
        // 文字顶部与基线距离
        float ascent = Math.abs(fontMetrics.ascent);
        // 文字底部与基线距离
        float descent = Math.abs(fontMetrics.descent);
        // 文字高度
        float fontHeight = ascent + descent;
        // 文字竖直中心点距离基线的距离
        float offsetY = fontHeight / 2 - Math.abs(fontMetrics.descent);
        // 文字宽度
        float fontWidth;

        String h = "12";
        // y轴坐标：-（半径 - 特殊刻度长度 - 特殊刻度宽度（作为间距）- 文字顶部距离基线的距离）
        float y = -(mRadius - mParticularScaleLength - mParticularScaleWidth - ascent);
        canvas.drawText(h, 0, y, mTextPaint);

        h = "3";
        fontWidth = mTextPaint.measureText(h);
        // y轴坐标：半径 - 特殊刻度长度 - 特殊刻度宽度 - 文字长度/2（绘制原点在文字横向中心）
        y = mRadius - mParticularScaleLength - mParticularScaleWidth - (fontWidth / 2);
        canvas.drawText(h, y, 0 + offsetY, mTextPaint);

        h = "6";
        // y轴坐标：半径 - 特殊刻度长度 - 特殊刻度宽度 - 文字底部与基线的间距
        y = mRadius - mParticularScaleLength - mParticularScaleWidth - descent;
        canvas.drawText(h, 0, y, mTextPaint);

        h = "9";
        fontWidth = mTextPaint.measureText(h);
        // y轴坐标：-（半径 - 特殊刻度长度 - 特殊刻度宽度 - 文字长度/2）
        y = -(mRadius - mParticularScaleLength - mParticularScaleWidth - (fontWidth / 2));
        canvas.drawText(h, y, 0 + offsetY, mTextPaint);

    }

    /**
     * 绘制指针
     *
     * @param canvas
     */
    private void drawPointer(Canvas canvas) {
        drawHourPointer(canvas);
        drawMinutePointer(canvas);
        drawSecondPointer(canvas);
        mPointerPaint.setColor(mClockColor);
        // 绘制中心原点，需要在指针绘制完成后才能绘制
        canvas.drawCircle(0, 0, mPointRadius, mPointerPaint);
    }

    /**
     * 绘制时针
     *
     * @param canvas
     */
    private void drawHourPointer(Canvas canvas) {
        mPointerPaint.setStrokeWidth(mHourPointerWidth);
        mPointerPaint.setColor(mHourPointerColor);
        // 当前时间的总秒数
        float s = mH * 60 * 60 + mM * 60 + mS;
        // 百分比
        float per = s / (12 * 60 * 60);
        // 通过角度计算弧度值，因为时钟的角度起线是Y轴负方向，而View角度的起线是X轴正方向，所以要加270°
        float angle = 360 * per + 270;
        float x = (float) (mHourPointerLength * Math.cos(Math.PI * 2 / 360 * angle));
        float y = (float) (mHourPointerLength * Math.sin(Math.PI * 2 / 360 * angle));
        canvas.drawLine(0, 0, x, y, mPointerPaint);
    }

    /**
     * 绘制分针
     *
     * @param canvas
     */
    private void drawMinutePointer(Canvas canvas) {
        mPointerPaint.setStrokeWidth(mMinutePointerWidth);
        mPointerPaint.setColor(mMinutePointerColor);
        float s = mM * 60 + mS;
        float per = s / (60 * 60);
        float angle = 360 * per + 270;
        float x = (float) (mMinutePointerLength * Math.cos(Math.PI * 2 / 360 * angle));
        float y = (float) (mMinutePointerLength * Math.sin(Math.PI * 2 / 360 * angle));
        canvas.drawLine(0, 0, x, y, mPointerPaint);
    }

    /**
     * 绘制秒针
     *
     * @param canvas
     */
    private void drawSecondPointer(Canvas canvas) {
        mPointerPaint.setStrokeWidth(mSecondPointerWidth);
        mPointerPaint.setColor(mSecondPointerColor);
        float s = mS;
        float per = s / 60;
        float angle = 360 * per + 270;
        float x = (float) (mSecondPointerLength * Math.cos(Math.PI * 2 / 360 * angle));
        float y = (float) (mSecondPointerLength * Math.sin(Math.PI * 2 / 360 * angle));
        canvas.drawLine(0, 0, x, y, mPointerPaint);
    }

    /**
     * 开始计时
     */
    private void startTime() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    getTime();
                }
            }
        }).start();
    }

    /**
     * 获取当前系统时间
     */
    private void getTime() {
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR);
        hour = hour % 12;
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);
        if (hour != mH | minute != mM | second != mS) {
            setTime(hour, minute, second);
            postInvalidate();
        }
    }

    /**
     * 设置时间
     *
     * @param h
     * @param m
     * @param s
     */
    private void setTime(int h, int m, int s) {
        mH = h;
        mM = m;
        mS = s;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        mCenterX = w / 2; // 将中心点x坐标放到横向屏幕中间
        mCenterY = h / 2; // 将中心点y坐标放到纵向屏幕中间
        mRadius = (float) (w / 2 * 0.8); // 中心点大小
        initClockPointerLength();
    }

    /**
     * 根据控件大小，初始化始终刻度的长度和宽度、指针的长度和宽度、时钟中心点的半径
     */
    private void initClockPointerLength() {
        /**
         * 默认时钟刻度长 = 半径 / 10
         * 默认时钟刻度宽 = 长 / 6
         */
        mDefaultScaleLength = mRadius / 10;
        mDefaultScaleWidth = mDefaultScaleLength / 6;

        /**
         * 特殊时刻刻度长 = 半径 / 5
         * 特殊时刻刻度宽 = 长 /6
         */
        mParticularScaleLength = mRadius / 5;
        mParticularScaleWidth = mParticularScaleLength / 6;

        /**
         * 时针长 = 半径 / 3
         * 时针宽 = 特殊时刻刻度宽
         */
        mHourPointerLength = mRadius / 3;
        mHourPointerWidth = mParticularScaleWidth;

        /**
         * 分针长 = 半径 / 2
         * 分针宽 = 特殊时刻刻度宽
         */
        mMinutePointerLength = mRadius / 2;
        mMinutePointerWidth = mParticularScaleWidth;

        /**
         * 秒针长 = 半径 / 3 * 2
         * 秒针宽 = 默认时刻刻度宽
         */
        mSecondPointerLength = mRadius / 3 * 2;
        mSecondPointerWidth = mDefaultScaleWidth;

        /**
         * 中心点半径 = （默认刻度宽 + 特殊时刻刻度宽） / 2
         */
        mPointRadius = (mDefaultScaleWidth + mParticularScaleWidth) / 2;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getMeasureSize(true,widthMeasureSpec);
        int height = getMeasureSize(false,heightMeasureSpec);
        setMeasuredDimension(width,height);
    }

    /**
     * 获取View尺寸
     * 基本上是标准写法
     * @param isWidth 是否是width，不是的话就是height
     * @param measureSpec
     * @return
     */
    private int getMeasureSize(boolean isWidth,int measureSpec) {
        int result = 0;
        int specSize = MeasureSpec.getSize(measureSpec);
        int specMode = MeasureSpec.getMode(measureSpec);
        switch (specMode){
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.AT_MOST:
                if (isWidth) {
                    result = Math.min(specSize,mWidth);
                }else {
                    result = Math.min(specSize,mHeight);
                }
                break;
            case MeasureSpec.UNSPECIFIED:
                if (isWidth) {
                    result = getSuggestedMinimumWidth();
                }else {
                    result = getSuggestedMinimumHeight();
                }
                break;
        }
        return result;
    }

    public int dp2Px(int dp) {
        float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5F);
    }
}
